const { Op, literal } = require('sequelize')
const Mysequelize = require('@/models')
const R = require('@/models/Response')
const { Source, BinSource, User, Share } = require('@/models/model')
const { ERROR_SOURCE_CODE } = require('@/constants/errorCode')
const { SEPARATE_CHAR } = require('@/constants/index')
const { generatId } = require('@/utils/idUtil')
const { dumpCheck } = require('@/utils/dumpCheck')
const { formatFilename } = require('@/utils/format')
const WpFile = require('@/models/fileModel')
const { fileTypeList } = require('@/constants/fileTypes')

// Source.belongsTo(Source, { foreignKey: 'parentId', as: 'parent' })

const commonSourceAttr = {
  include: [
    [
      Mysequelize.literal(
        '(SELECT COUNT(*) FROM source AS sc WHERE sc.parent_id = source.id and sc.is_dir = 0)'
      ),
      'hasFile'
    ],
    [
      Mysequelize.literal(
        '(SELECT COUNT(*) FROM source AS sc WHERE sc.parent_id = source.id and sc.is_dir = 1)'
      ),
      'hasFolder'
    ],
    [
      Mysequelize.literal(
        `CASE WHEN EXISTS (SELECT 1 FROM source as sc WHERE sc.parent_id = source.id and sc.delete_flag=0) THEN 0 ELSE 1 END`
      ),
      'isLeaf'
    ]
  ]
}
const commonSourceIncludes = [
  {
    model: User,
    as: 'user',
    attributes: ['id', 'username', 'avator']
  },
  {
    model: WpFile,
    as: 'file',
    attributes: ['id', 'filename', 'size']
  },
  {
    model: Share,
    attributes: ['id', 'title', 'isShareTo', 'isLink']
  }
]

/**
 * 查询用户文件列表接口
 * @param {*} ctx
 */
module.exports.listSource = async (ctx) => {
  // 到达这里，说明查询参数没有问题
  const { page, pageSize, filename, isDir, deleteFlag, parentId } = ctx.newQuery
  const start = (page - 1) * pageSize

  const whereCondition = {
    parentId: BigInt(parentId),
    deleteFlag: 0
  }

  if (filename !== undefined) {
    whereCondition.filename = {
      [Op.like]: `%${filename}%`
    }
  }

  if (isDir !== undefined) {
    whereCondition.isDir = isDir
  }

  if (deleteFlag !== undefined) {
    whereCondition.deleteFlag = deleteFlag
  }

  // 查询一份自己的信息，parentId对应的信息
  let curSource = await Source.findByPk(BigInt(parentId), {
    attributes: commonSourceAttr,
    include: commonSourceIncludes
  })

  const parentNames = curSource.parentLevel
    .split(SEPARATE_CHAR)
    .filter((item) => item !== '-1')
    .concat([curSource.id])
    .map((item) => BigInt(item))

  // 查询出相关的信息
  const ppSources = await Source.findAll({
    where: {
      id: {
        [Op.in]: parentNames
      }
    },
    attribute: ['id', 'filename'],
    order: [
      Mysequelize.literal(`FIELD(id, ${parentNames.join(',')})`) // 按指定 ID 数组顺序排序
    ]
  })

  const curPathInfo = ppSources.map((item) => {
    return {
      id: item.id,
      filename: item.filename
    }
  })

  curSource = curSource.toJSON()
  curSource.pathInfo = curPathInfo

  let { rows, count } = await Source.findAndCountAll({
    where: whereCondition,
    attributes: commonSourceAttr,
    include: commonSourceIncludes,
    order: [['updateTime', 'DESC']],
    limit: +pageSize,
    offset: +start
  })

  // 对rows的数据进行修改，添加pathInfo信息

  rows = rows.map((item) => {
    let res = item.toJSON()
    return {
      ...res,
      pathInfo: curPathInfo.concat([
        {
          id: item.id,
          filename: item.filename
        }
      ])
    }
  })

  ctx.body = R.success({
    page,
    pageSize,
    items: rows,
    total: count,
    curSource,
    pages: Math.ceil(count / pageSize)
  })
}

/**
 * 根据文件类型来查询个人空间下的所有文件
 */
module.exports.listFileTypeSource = async (ctx) => {
  // 到达这里，说明查询参数没有问题
  const { page, pageSize, fileType, filename } = ctx.newQuery
  const start = (page - 1) * pageSize

  // 根据fileType可能的类型来进行处理
  const type = fileTypeList.find((item) => item.name === fileType)

  // 将当前的类型也作为一个curSource?

  const whereCondition = {
    type: {
      [Op.in]: type.type
    },
    deleteFlag: 0,
    isDir: 0,
    targetType: 1,
    targetId: BigInt(ctx.user.id)
  }

  if (filename !== undefined) {
    whereCondition.filename = {
      [Op.like]: `%${filename}%`
    }
  }

  let { rows, count } = await Source.findAndCountAll({
    where: whereCondition,
    attributes: commonSourceAttr,
    include: commonSourceIncludes,
    order: [['updateTime', 'DESC']],
    distinct: true,
    limit: +pageSize,
    offset: +start
  })

  // 对rows进行处理,添加pathInfo信息

  let finaRows = []
  for (let i = 0; i < rows.length; i++) {
    let res = rows[i].toJSON()
    // 查询parent_level对应的信息
    const psources = await Source.findAll({
      where: {
        id: {
          [Op.in]: rows[i].parentLevel.split(SEPARATE_CHAR)
        }
      },
      attribute: ['id', 'filename'],
      order: [
        Mysequelize.literal(`FIELD(id, ${rows[i].parentLevel})`) // 按指定 ID 数组顺序排序
      ]
    })
    res.pathInfo = psources
      .map((v) => ({
        id: v.id,
        filename: v.filename
      }))
      .concat({
        id: rows[i].id,
        filename: rows[i].filename
      })
    finaRows.push(res)
  }

  ctx.body = R.success({
    page,
    pageSize,
    items: finaRows,
    total: count,
    pages: Math.ceil(count / pageSize)
  })
}

/**
 * 查询回收站里面的文件
 * 1. 操作者需要是本人，createUser
 */
module.exports.listBinSource = async (ctx) => {
  // 到达这里，说明查询参数没有问题
  const { page, pageSize } = ctx.newQuery
  const start = (page - 1) * pageSize

  const commonIncludes = [
    {
      model: User,
      as: 'user',
      attributes: ['id', 'username', 'avator']
    },
    {
      model: Source,
      as: 'source',
      attributes: commonSourceAttr,
      include: commonSourceIncludes
    }
  ]

  const whereCondition = {
    createUser: BigInt(ctx.user.id)
  }

  let { rows, count } = await BinSource.findAndCountAll({
    where: whereCondition,
    attributes: ['id', 'createUser', 'parentLevel'],
    include: commonIncludes,
    order: [['updateTime', 'DESC']],
    distinct: true,
    limit: +pageSize,
    offset: +start
  })

  // 对rows进行处理,添加pathInfo信息

  let finaRows = []
  for (let i = 0; i < rows.length; i++) {
    let res = rows[i].toJSON()

    // 查询parent_level对应的信息
    const psources = await Source.findAll({
      where: {
        id: {
          [Op.in]: rows[i].parentLevel.split(SEPARATE_CHAR)
        }
      },
      attribute: ['id', 'filename'],
      order: [
        Mysequelize.literal(`FIELD(id, ${rows[i].parentLevel})`) // 按指定 ID 数组顺序排序
      ]
    })
    res = {
      ...res.source,
      binSourceId: res.id,
      createUser: res.user
    }
    res.pathInfo = psources
      .map((v) => ({
        id: v.id,
        filename: v.filename
      }))
      .concat({
        id: rows[i].source.id,
        filename: rows[i].source.filename
      })
    finaRows.push(res)
  }

  ctx.body = R.success({
    page,
    pageSize,
    items: finaRows,
    total: count,
    pages: Math.ceil(count / pageSize)
  })
}

/**
 * 用户创建文件夹接口
 * @param {*} ctx
 */
module.exports.createDir = async (ctx) => {
  // 走到这里，说明参数正常，直接创建即可
  // 创建流程：继承pSource的targetId和targetType
  const { dirName, parentId } = ctx.request.body
  const t = await Mysequelize.transaction()
  try {
    const id = generatId()
    const pSource = await Source.findByPk(BigInt(parentId), {
      attributes: ['targetType', 'targetId', 'parentLevel']
    })
    await Source.create(
      {
        id,
        targetType: pSource.targetType,
        targetId: pSource.targetId,
        filename: dirName,
        parentId: parentId,
        isDir: 1,
        fileId: -1,
        type: 0,
        sizeDesc: '--',
        deleteFlag: 0,
        parentLevel: pSource.parentLevel + SEPARATE_CHAR + parentId,
        createUser: ctx.user.id,
        updateUser: ctx.user.id
      },
      {
        transaction: t
      }
    )
    await t.commit()
    ctx.body = R.success()
  } catch (err) {
    await t.rollback()
    ctx.body = R.error(ERROR_SOURCE_CODE.CREATE_DIR_ERROR)
  }
}

/**
 * 用户新建文件接口
 */
module.exports.createFile = async (ctx) => {
  const { filename, parentId, type } = ctx.request.body
  const t = await Mysequelize.transaction()
  try {
    const id = generatId()
    const pSource = await Source.findByPk(BigInt(parentId), {
      attributes: ['targetType', 'targetId', 'parentLevel']
    })
    await Source.create(
      {
        id,
        targetType: pSource.targetType,
        targetId: pSource.targetId,
        filename: filename,
        parentId: parentId,
        isDir: 0,
        fileId: -1,
        type,
        sizeDesc: '0',
        deleteFlag: 0,
        parentLevel: pSource.parentLevel + SEPARATE_CHAR + parentId,
        createUser: ctx.user.id,
        updateUser: ctx.user.id
      },
      {
        transaction: t
      }
    )

    // TODO需要在当前存储源的文件目录下创建同名文件
    await t.commit()
    ctx.body = R.success()
  } catch (err) {
    await t.rollback()
    ctx.body = R.error(ERROR_SOURCE_CODE.CREATE_FILE_ERROR)
  }
}

/**
 * 用户文件重命名接口
 * @param {*} ctx
 */
module.exports.rename = async (ctx) => {
  // 走到这里，说明参数没有问题，可以直接更新文件名
  const { filename, sourceId } = ctx.request.body
  const t = await Mysequelize.transaction()
  try {
    await Source.update(
      {
        filename
      },
      {
        where: {
          id: BigInt(sourceId)
        },
        transaction: t
      }
    )
    await t.commit()
    ctx.body = R.success()
  } catch {
    await t.rollback()
    ctx.body = R.error(ERROR_SOURCE_CODE.RENAME_ERROR)
  }
}

/**
 * 用户修改被移动文件的parentId
 * @param {*} parentId 目标目录ID
 * @param {*} sourceId 用户-文件ID
 * @param {*} t 事务对象
 * @param {*} filename 文件名
 */
const updateFrom = async (parentId, sourceId, t, filename) => {
  try {
    await Source.update(
      {
        filename,
        parentId: BigInt(parentId)
      },
      {
        where: {
          id: BigInt(sourceId)
        },
        transaction: t
      }
    )
  } catch (e) {
    throw e
  }
}

/**
 * 在对方目录创建新文件记录或者修改对方目录的文件记录
 * @param {*} parentId 目标目录
 * @param {*} userId 用户ID
 * @param {*} source to文件实体
 * @param {*} t 事务对象
 * @param {*} filename 新名称
 * @param {*} isCreate 创建还是更新
 */
const updateTo = async (
  parentId,
  userId,
  source,
  t,
  filename,
  isCreate = false
) => {
  userId = BigInt(userId)
  if (isCreate) {
    await Source.create(
      {
        fileId: BigInt(source.fileId),
        isDir: source.isDir,
        type: source.type,
        filename,
        sizeDesc: source.sizeDesc,
        deleteFlag: source.deleteFlag,
        parentId: BigInt(parentId),
        id: generatId(),
        userId,
        createTime: Date.now(),
        createUser: userId,
        updateTime: Date.now(),
        updateUser: userId
      },
      {
        transaction: t
      }
    )
  } else {
    await Source.update(
      {
        fileId: BigInt(source.fileId),
        isDir: source.isDir,
        type: source.type,
        filename,
        sizeDesc: source.sizeDesc,
        deleteFlag: source.deleteFlag,
        createUser: BigInt(userId),
        updateUser: BigInt(userId)
      },
      {
        where: {
          id: BigInt(source.dump.id)
        },
        transaction: t
      }
    )
  }
}

const levelUpdateSource = async (rootSource, pSource, userId, t) => {
  // 例如B移动到G
  // B:A--->ACG
  // D:AB---->ACGB
  // m:ABD---->ACGBD

  // 进行层次更新树
  // 1. 更新B，也就是根
  await Source.update(
    {
      targetType: pSource.targetType,
      targetId: pSource.targetId,
      filename: rootSource.filename,
      parentId: pSource.id,
      parentLevel: pSource.parentLevel + SEPARATE_CHAR + pSource.id,
      updateUser: userId
    },
    {
      where: {
        id: BigInt(rootSource.id)
      },
      transaction: t
    }
  )

  // 2. 更新后代的parentLevel
  const children = await Source.findAll({
    where: {
      parentLevel: {
        [Op.like]: rootSource.parentLevel + SEPARATE_CHAR + rootSource.id + '%'
      }
    }
  })

  const beforSplit = rootSource.parentLevel + SEPARATE_CHAR + rootSource.id
  const newPrefix =
    pSource.parentLevel +
    SEPARATE_CHAR +
    pSource.id +
    SEPARATE_CHAR +
    rootSource.id
  for (let i = 0; i < children.length; i++) {
    let item = children[i]

    // A,B,D  split A,B  ----->,D，得到后半部分不变的
    const staticPart = children[i].parentLevel.split(beforSplit)[1]

    // 新的替换
    const newParentLevel = newPrefix + staticPart
    await Source.update(
      {
        targetType: pSource.targetType,
        targetId: pSource.targetId,
        parentLevel: newParentLevel,
        updateUser: userId
      },
      {
        where: {
          id: BigInt(item.id)
        },
        transaction: t
      }
    )
  }
}

/**
 * 用户移动文件接口
 * @param {*} ctx
 */
module.exports.moveFile = async (ctx) => {
  // 到达这里，说明参数没有问题，可以进行移动了
  const t = await Mysequelize.transaction()
  const { sources, parentId } = ctx.request.body
  // 多文件同时移动，要么全部成功，要么全部失败
  for (let i = 0; i < sources.length; i++) {
    let type = +sources[i].type
    let source = sources[i].entity
    // 1. 没有重名文件的情况
    if (!source.dump) {
      // 直接修改parentID即可
      try {
        await updateFrom(parentId, source.id, t)
        continue
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.OVERVIDE_ERROR)
        return
      }
    }

    // 2. 出现了重名文件
    if (type === 1) {
      // 2.1 直接覆盖（只能都文件类型才可以）
      if (source.isDir || source.dump.isDir) {
        // 不可直接覆盖
        ctx.body = R.error(ERROR_SOURCE_CODE.OVERVIDE_ERROR)
        await t.rollback()
        return
      }
      // 都是文件类型了，可以覆盖
      // from记录被删除
      try {
        await Source.destroy({
          where: {
            id: BigInt(source.id)
          },
          transaction: t
        })
        // to记录修改内容
        await updateTo(parentId, ctx.user.id, source, t, source.filename)
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.MOVE_FILE_ERROR)
        return
      }
    } else if (type === 2) {
      // 2.2 创建副本(任意类型都可)
      // to记录保留
      // from记录修改filename和parentId
      try {
        await updateFrom(parentId, source.id, t, source.myfilename)
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.MOVE_FILE_ERROR)
        return
      }
    }
  }
  await t.commit()
  ctx.body = R.success()
}

/**
 * 用户移动文件接口
 * @param {*} ctx
 */
module.exports.moveFile2 = async (ctx) => {
  // 到达这里，说明参数没有问题，可以进行移动了
  const t = await Mysequelize.transaction()
  const { sourceIds, parentId } = ctx.request.body
  // 多文件同时移动，要么全部成功，要么全部失败
  for (let i = 0; i < sourceIds.length; i++) {
    const source = await Source.findByPk(BigInt(sourceIds[i]))
    const res = await dumpCheck.checkFilename(ctx, parentId, source.filename)

    const updateSource = async (sourceId, filename) => {
      await Source.update(
        {
          targetType: parentSource.targetType,
          targetId: parentSource.targetId,
          filename: filename,
          parentId: parentId,
          parentLevel: parentSource.parentLevel + SEPARATE_CHAR + parentId,
          updateUser: ctx.user.id
        },
        {
          where: {
            id: BigInt(sourceId)
          },
          transaction: t
        }
      )
    }

    if (type === 0) {
      if (res) {
        // 跳过未重名时，直接新增
        await updateSource(source.id, source.filename)
      }
    } else if (type === 1) {
      if (res) {
        await updateSource(source.id, source.filename)
      } else {
        // 需要更新重名的文件
        await Source.update(
          {
            isDir: source.isDir,
            fileId: source.fileId,
            type: source.type,
            sizeDesc: source.sizeDesc,
            deleteFlag: source.deleteFlag,
            updateUser: ctx.user.id
          },
          {
            where: {
              parentId: BigInt(parentId),
              filename: source.filename
            },
            transaction: t
          }
        )

        // 删除原先的记录
        await Source.destroy({
          where: {
            id: BigInt(source.id)
          }
        })
      }
    } else if (type === 2) {
      if (res) {
        await updateSource(source.id, source.filename)
      } else {
        // 创建一个新的名字
        const newName = formatFilename(source.filename)
        await updateSource(source.id, newName)
      }
    }
  }
  await t.commit()
  ctx.body = R.success()
}

/**
 * 用户复制文件接口
 */

module.exports.copyFile = async (ctx) => {
  // 到达这里，说明参数没有问题，可以复制了
  const t = await Mysequelize.transaction()
  const { sources, parentId } = ctx.request.body
  // 多文件同时复制，要么全部成功，要么全部失败
  for (let i = 0; i < sources.length; i++) {
    let type = +sources[i].type
    let source = sources[i].entity
    // 1. 没有重名文件的情况
    if (!source.dump) {
      // 直接在目标目录创建一个新的文件记录
      try {
        await updateTo(parentId, ctx.user.id, source, t, source.filename, true)
        continue
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.OVERVIDE_ERROR)
        return
      }
    }

    // 2. 出现了重名文件
    if (type === 1) {
      // 2.1 直接覆盖（只能都文件类型才可以）
      if (source.isDir || source.dump.isDir) {
        // 不可直接覆盖
        ctx.body = R.error(ERROR_SOURCE_CODE.OVERVIDE_ERROR)
        await t.rollback()
        return
      }
      // 都是文件类型了，可以覆盖
      // from记录无需删除
      try {
        // to记录修改内容
        await updateTo(parentId, ctx.user.id, source, t, source.filename)
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.MOVE_FILE_ERROR)
        return
      }
    } else if (type === 2) {
      // 2.2 创建副本(任意类型都可)
      // to记录新增
      // from记录保留
      try {
        await updateTo(
          parentId,
          ctx.user.id,
          source,
          t,
          source.myfilename,
          true
        )
      } catch (e) {
        await t.rollback()
        ctx.body = R.error(ERROR_SOURCE_CODE.MOVE_FILE_ERROR)
        return
      }
    }
  }
  await t.commit()
  ctx.body = R.success()
}

const levelCreateSource = async (rootSource, pSource, userId, t) => {
  // 进行层次建立树
  if (!rootSource) {
    return []
  }

  // 对rootSource进行处理
  rootSource = {
    ...rootSource,
    targetType: pSource.targetType,
    targetId: pSource.targetId,
    parentId: pSource.id,
    parentLevel: pSource.parentLevel + SEPARATE_CHAR + pSource.id,
    createUser: userId,
    updateUser: userId
  }

  const result = []
  const queue = [rootSource]
  while (queue.length > 0) {
    const source = queue.pop()
    // 复制该source
    const newId = generatId()
    const createObj = {
      id: newId,
      targetType: source.targetType,
      targetId: source.targetId,
      filename: source.filename,
      parentId: source.parentId,
      isDir: source.isDir,
      fileId: source.fileId,
      type: source.type,
      sizeDesc: source.sizeDesc,
      deleteFlag: source.deleteFlag,
      parentLevel: source.parentLevel,
      createUser: source.createUser,
      updateUser: source.updateUser
    }

    result.push(createObj)

    // 查询出当前source的children
    const children = await Source.findAll({
      where: {
        parentId: source.id,
        deleteFlag: 0
      }
    })

    // 对children进行修改
    const newChildren = children.map((item) => {
      return {
        ...item,
        parentId: newId,
        targetType: source.targetType,
        targetId: source.targetId,
        parentLevel: source.parentLevel + SEPARATE_CHAR + newId,
        createUser: source.createUser,
        updateUser: source.updateUser
      }
    })

    queue.push(...newChildren)
  }

  await Source.bulkCreate(result, { transaction: t })
}

/**
 * 用户复制文件接口
 */
module.exports.copyFile2 = async (ctx) => {
  // 到达这里，说明参数没有问题，可以复制了
  // 复制时的重名文件统一处理，由用户的配置决定，跳过，覆盖，生成副本
  const type = 0
  const t = await Mysequelize.transaction()
  const t2 = await Mysequelize.transaction({
    isolationLevel: Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED
  })
  const { sourceIds, parentId } = ctx.request.body
  const parentSource = await Source.findByPk(parentId)
  // 多文件同时复制，要么全部成功，要么全部失败

  try {
    for (let i = 0; i < sourceIds.length; i++) {
      const source = await Source.findByPk(BigInt(sourceIds[i]))
      const res = await dumpCheck.checkFilename(ctx, parentId, source.filename)

      // 如果该source为文件夹，需要递归创建子级
      if (type === 0) {
        if (res) {
          // 跳过 (未重名时，直接新增)
          await levelCreateSource(source, parentSource, ctx.user.id, t)
        }
      } else if (type === 1) {
        if (res) {
          await levelCreateSource(source, parentSource, ctx.user.id, t)
        } else {
          // 删除原先的source，重新新增
          const beforeSource = await Source.findOne({
            where: {
              parentId: BigInt(parentId),
              filename: source.filename,
              deleteFlag: 0
            },
            transaction: t2
          })
          if (beforeSource) {
            await Source.destroy({
              where: {
                id: BigInt(beforeSource.id)
              },
              transaction: t
            })

            // 删除后代
            await Source.destroy({
              where: {
                parentLevel:
                  beforeSource.parentLevel +
                  SEPARATE_CHAR +
                  beforeSource.id +
                  '%'
              }
            })
          }
        }
      } else if (type === 2) {
        if (res) {
          await createSource(source.filename)
        } else {
          // 创建一个新的名字
          const newName = formatFilename(source.filename)
          await createSource(newName)
        }
      }
    }
    await t.commit()
    ctx.body = R.success()
  } catch (error) {
    await t.rollback()
    ctx.body = R.error(ERROR_SOURCE_CODE.COPY_SOURCE_ERROR)
  }
}

/**
 * 根据传入的binSourceIds，获取父子依赖关系，取出第一层
 * @param {*} isAll
 * @param {*} binSourceIds
 */
const getNeedRecOrDelArr = async (userId, isAll, binSourceIds) => {
  binSourceIds = binSourceIds.map((v) => BigInt(v))
  let binSources = []
  if (isAll) {
    binSources = await BinSource.findAll({
      where: {
        createUser: BigInt(userId)
      },
      attributes: ['id', 'parentLevel', 'sourceId']
    })
  } else {
    binSources = await BinSource.findAll({
      where: {
        id: {
          [Op.in]: binSourceIds
        }
      },
      attributes: ['id', 'parentLevel', 'sourceId']
    })
  }

  // 进行父子依赖关系分析，还原第一层
  const needRecycleArr = binSources.filter((item) => {
    // 如何证明它是第一层？ 条件：没有其他的parentLevel为它的子集
    // 不存在说明该item的parentLevel不包含任何人，说明它是根
    return !binSources.some((v) => {
      // 是否存在一个数据，当前item，startwith其他人，如果startwith了，说明不是根
      return (
        item.parentLevel.startsWith(v.parentLevel) &&
        item.parentLevel !== v.parentLevel
      )
    })
  })

  return {
    binSources,
    needRecycleArr
  }
}

/**
 * 用户彻底删除文件接口
 * @param {} ctx
 */
module.exports.delFile = async (ctx) => {
  let { binSourceIds, isAll } = ctx.request.body
  // 递归删除这些用户-文件ID对应的记录
  // TODO 对应的文件记录是否要删除？否，其他用户有关联的，需要所有用户都没有关联才会删除，可以
  // 使用定时任务每天删除一下

  // 存储所有要删除的文件-记录ID
  // 防止出现重复删除，其实出现了也没有关系，但是数据库操作可能更耗时
  // a文件夹下存在b文件夹，b文件夹先放入回收站，a文件夹后放入，就有可能重复

  // 删除逻辑：自己删除，其后代也被删除，删除后，bin-source记录需要删除，后代bin-source记录也需要删除
  const { needRecycleArr, binSources } = await getNeedRecOrDelArr(
    ctx.user.id,
    isAll,
    binSourceIds
  )

  for (let i = 0; i < needRecycleArr.length; i++) {
    let item = needRecycleArr[i]

    try {
      const t = await Mysequelize.transaction()
      // 可以进行删除
      await Source.destroy({
        where: {
          id: item.sourceId
        },
        transaction: t
      })

      // 将其后代也全部删除
      await Source.destroy({
        where: {
          parentLevel: {
            [Op.like]: item.parentLevel + SEPARATE_CHAR + item.sourceId + '%'
          }
        },
        transaction: t
      })

      // 删除完后，bin-source记录需要清除,存在父子关系的bin-source也需要清除
      await BinSource.destroy({
        where: {
          id: item.id
        },
        transaction: t
      })

      const child = binSources
        .filter((v1) =>
          v1.parentLevel.startsWith(
            item.parentLevel + SEPARATE_CHAR + item.sourceId
          )
        )
        .map((v2) => BigInt(v2.id))

      await BinSource.destroy({
        where: {
          id: {
            [Op.in]: child
          }
        },
        transaction: t
      })
      await t.commit()
    } catch (error) {
      await t.rollback()
    }
  }
  ctx.body = R.success()
}

/**
 * 文件放入回收站接口
 * @param {} ctx
 */
module.exports.binFile = async (ctx) => {
  // 到达这里，说明所有文件可以被放入回收站
  // 自身deleteFlag设置为1，其后代也要设置为1
  const t = await Mysequelize.transaction()
  let { sourceIds } = ctx.request.body
  sourceIds = sourceIds.map((item) => BigInt(item))

  const sources = await Source.findAll({
    where: {
      id: {
        [Op.in]: sourceIds
      }
    },
    attributes: ['id', 'targetId', 'targetType', 'parentLevel']
  })

  // 删除的本身加入到回收站记录
  // 例如当前的source的parentLevel为-1,1,2
  // 那么它的后代，-1,1,2,x,x,x
  try {
    await Source.update(
      {
        deleteFlag: 1
      },
      {
        where: {
          id: {
            [Op.in]: sourceIds
          }
        },
        transaction: t
      }
    )

    for (let i = 0; i < sources.length; i++) {
      const nextId = generatId()
      await BinSource.create(
        {
          id: nextId,
          targetId: sources[i].targetId,
          targetType: sources[i].targetType,
          sourceId: sources[i].id,
          parentLevel: sources[i].parentLevel,
          createUser: ctx.user.id,
          updateUser: ctx.user.id
        },
        {
          transaction: t
        }
      )

      // 对应的后代
      await Source.update(
        {
          deleteFlag: 1
        },
        {
          where: {
            parentLevel: {
              [Op.like]:
                sources[i].parentLevel + SEPARATE_CHAR + sources[i].id + '%'
            }
          },
          transaction: t
        }
      )
    }

    await t.commit()
    ctx.body = R.success()
  } catch {
    await t.rollback()
    ctx.body = R.error(ERROR_SOURCE_CODE.BIN_FILE_ERROR)
  }
}

/**
 * 用户还原在回收站中的文件接口
 */
module.exports.recFile = async (ctx) => {
  let { isAll, binSourceIds } = ctx.request.body

  binSourceIds = binSourceIds.map((v) => BigInt(v))
  let binSources = []
  if (isAll) {
    binSources = await BinSource.findAll({
      where: {
        createUser: BigInt(ctx.user.id)
      },
      attributes: ['id', 'parentLevel', 'sourceId']
    })
  } else {
    binSources = await BinSource.findAll({
      where: {
        id: {
          [Op.in]: binSourceIds
        }
      },
      attributes: ['id', 'parentLevel', 'sourceId']
    })
  }

  // 进行父子依赖关系分析，还原第一层
  const needRecycleArr = binSources.filter((item) => {
    // 如何证明它是第一层？ 条件：没有其他的parentLevel为它的子集
    // 不存在说明该item的parentLevel不包含任何人，说明它是根
    return !binSources.some((v) => {
      // 是否存在一个数据，当前item，startwith其他人，如果startwith了，说明不是根
      return (
        item.parentLevel.startsWith(v.parentLevel) &&
        item.parentLevel !== v.parentLevel
      )
    })
  })

  // 将根进行还原，还原时需要注意其祖先是否在回收站
  // 我现在在还原的资源已经是当前用户能还原的根了，不排除其他用户把其祖先已经放入回收站的情况

  for (let i = 0; i < needRecycleArr.length; i++) {
    let item = needRecycleArr[i]
    // 查询出为当前item的parentLevel的字串，也就是其父亲，自己肯定对应的上
    const res = await BinSource.findOne({
      where: {
        parentLevel: {
          [Op.notLike]: item.parentLevel
        },
        [Op.and]: literal(`LOCATE('parentLevel', ${item.parentLevel}) > 0`)
      }
    })
    if (res) {
      // 说明存在祖先,不能还原，跳过
    } else {
      try {
        const t = await Mysequelize.transaction()
        // 可以进行还原了
        await Source.update(
          {
            deleteFlag: 0
          },
          {
            where: {
              id: item.sourceId
            },
            transaction: t
          }
        )

        // 将其后代也全部设置为0
        await Source.update(
          {
            deleteFlag: 0
          },
          {
            where: {
              parentLevel: {
                [Op.like]:
                  item.parentLevel + SEPARATE_CHAR + item.sourceId + '%'
              }
            },
            transaction: t
          }
        )

        // 更新完后，bin-source记录需要清除,存在父子关系的bin-source也需要清除
        await BinSource.destroy({
          where: {
            id: item.id
          },
          transaction: t
        })

        const child = binSources
          .filter((v1) =>
            v1.parentLevel.startsWith(
              item.parentLevel + SEPARATE_CHAR + item.sourceId
            )
          )
          .map((v2) => BigInt(v2.id))

        await BinSource.destroy({
          where: {
            id: {
              [Op.in]: child
            }
          },
          transaction: t
        })
        await t.commit()
      } catch (error) {
        await t.rollback()
      }
    }
  }

  ctx.body = R.success()
  // 1. 对于传入的source列表，将source及其子source的deleteFlag改为0
  // 2. 如果还原到的目录也被删除？直接跳过改source，不进行处理
  // 全部还原，查询出binSourceIds对应的全部source，进行父子依赖分析，删除第一层，删除所有bin-source-id记录
  // 还原时，如果其祖先也在回收站中，则不能还原
  // 还原成功时，如果有后代记录在回收站表中，需要清除
}
